<?php
// +----------------------------------------------------------------------
// | Version 自定义服务
// +----------------------------------------------------------------------
// | 主要服务于Version数据处理
// +----------------------------------------------------------------------
// | 错误编码头 213xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\model\CosMediaModel;
use common\model\ReviewRequestNodeModel;
use common\model\StepCategoryModel;
use common\model\VersionCheckListModel;
use malkusch\lock\mutex\PHPRedisMutex;
use common\exception\LogicException;
use common\model\ReviewRequestModel;
use common\model\ProjectModel;
use common\model\VersionModel;
use common\model\EntityModel;
use support\bootstrap\Redis;
use common\model\MediaModel;
use common\model\StatusModel;
use common\model\StepModel;
use common\model\UserModel;
use common\model\TaskModel;
use support\SingletonTrait;
use think\Exception;
use think\Hook;
use think\Model;
use think\model\RelationModel;
use think\module\Module;
use Webman\Stomp\Client;
use support\ErrorCode;
use function request;
use Throwable;

class VersionService
{
    /**
     * @var MediaModel
     */
    private $mediaModel;
    /**
     * @var CosMediaModel
     */
    private $cosMediaModel;
    /**
     * @var VersionModel
     */
    private $versionModel;

    // 单例工具
    use SingletonTrait;

    /**
     * VersionService constructor.
     */
    public function __construct()
    {
        $this->mediaModel = model(MediaModel::class);
        $this->cosMediaModel = model(CosMediaModel::class);
        $this->versionModel = model(VersionModel::class);
    }

    /**
     * 通过id查询版本
     * @param int $versionId
     * @return array|mixed|string
     */
    public function getVersionById(int $versionId)
    {
        $version = model(VersionModel::class)->find($versionId);
        if (empty($version)) {
            throw new LogicException('version not found', ErrorCode::VERSION_NOT_FOUND);
        }
        return $version;
    }

    /**
     * 审核室 查询版本信息
     * @param $param
     * @param $userId
     * @return array
     */
    public function getTaskVersionForReviewRoom($param, $userId)
    {
        $versionModuleData = Module::$moduleDictData['module_index_by_code']['version'] ?? [];
        $taskModuleData = Module::$moduleDictData['module_index_by_code']['task'] ?? [];

        $versionId = $param['filter']['version_id'];
        $reviewRequestId = $param['filter']['review_request_id'];
        if (empty($versionModuleData)) {
            throw_strack_exception("missing module data", ErrorCode::MISSING_MODULE_PARAM);
        }

        $versionFields = [
            "version.id",
            "version.number",
            "version.link_id",
            "version.created",
            "version.is_pass",
            "version.is_publish",
            "version.json",
            "user.id",
            "user.name",
            "user.email",
            "user.avatar",
            "user.phone",
        ];
        $version = $this
            ->versionModel
            ->join("user user ON user.id = version.created_by", "LEFT")
            ->where([
                'version.id' => $versionId,
                'version.module_id' => $taskModuleData['id'],
            ])
            ->field(build_complex_query_fields($versionFields, "__"))
            ->find();
        $versionJson = json_decode($version['version__json'], true);
        $versionData = separate_complex_query_data($version);
        $versionData['version_check_list'] = model(VersionCheckListModel::class)->where(['version_id' => $versionId])->select();

        if (isset($versionJson['version_media'])) {
            $versionData['version_media'] = model(MediaModel::class)->where(['id' => ['IN', $versionJson['version_media']]])->field("id,thumb,type,param")->select();
            $versionData['version_media'] = array_map(function ($mediaItem) {
                return CommonService::getFormatAttrData(model(MediaModel::class), $mediaItem);
            }, $versionData['version_media']);
        } else {
            $versionData['version_media'] = null;
        }


        //审批信息
        $reviewRequestModel = model(ReviewRequestModel::class);

        $reviewRequestFields = [
            "review_request.id",
            "review_request.name",
            "review_request.is_pass",
            "review_request.param",
            "review_request.created",
            "review_request_node.id as node_id",
            "review_request_node.role_code as node_role_code",
            "review_request_node.parent_id as node_parent_id",
            "review_request_node.plan_reviewer as node_plan_reviewer",
            "review_request_node.real_reviewer as node_real_reviewer",
            "review_request_node.is_pass as node_is_pass",
            "review_request_node.is_reviewed as node_is_reviewed",
            "review_request_node.is_active as node_is_active",
            "review_request_node.reviewed as node_reviewed",
        ];

        $reviewRequestData = $reviewRequestModel
            ->alias("review_request")
            ->join("__REVIEW_REQUEST_NODE__ AS review_request_node ON review_request.id = review_request_node.review_request_id", "LEFT")
            ->where([
                'review_request.link_id' => $versionId,
                'review_request.module_id' => $versionModuleData['id'],
                'review_request_node.plan_reviewer' => ['gt', 0],
                'review_request_node.review_request_id' => $reviewRequestId,
                "_logic" => "AND"
            ])
            ->order("review_request_node.id ASC")
            ->field($reviewRequestFields)
            ->select();

        $reviewerIds = array_column($reviewRequestData, 'node_plan_reviewer', 'node_plan_reviewer');
        $reviewerRealIds = array_column($reviewRequestData, 'node_real_reviewer', 'node_real_reviewer');
        $reviewerIds = array_merge($reviewerIds, $reviewerRealIds);
        $reviewerIds = array_filter($reviewerIds, function ($item) {
            return (int)$item > 0;
        });
        $userData = [];
        if (!empty($reviewerIds)) {
            $userModel = model(UserModel::class);
            $userData = $userModel->where(['id' => ['IN', implode(',', $reviewerIds)]])->field("id,name,email,phone,avatar")->select();
            $userData = array_column($userData, null, 'id');
        }

        $currentNode = null;
        $reviewRequest = [];
        foreach ($reviewRequestData as &$reviewNode) {

            if (empty($reviewRequest)) {
                $reviewRequest = [
                    "id" => $reviewNode['id'],
                    "name" => $reviewNode['name'],
                    "is_pass" => $reviewNode['is_pass'],
                    "param" => $reviewNode['param'],
                    "created" => $reviewNode['created'],
                ];
            }
            $tmp = [
                "id" => $reviewNode['node_id'],
                "role_code" => $reviewNode['node_role_code'],
                "parent_id" => $reviewNode['node_parent_id'],
                "plan_reviewer" => $reviewNode['node_plan_reviewer'],
                "real_reviewer" => $reviewNode['node_real_reviewer'],
                "is_pass" => $reviewNode['node_is_pass'],
                "is_reviewed" => $reviewNode['node_is_reviewed'],
                "is_active" => $reviewNode['node_is_active'],
                'reviewed' => $reviewNode['node_reviewed']
            ];
            if ($tmp['real_reviewer'] == $userId || $tmp['plan_reviewer'] == $userId) {
                $currentNode = $tmp;
            }
            if (isset($userData[$tmp['plan_reviewer']])) {
                $tmp['plan_reviewer'] = $userData[$tmp['plan_reviewer']];
            }
            if (isset($userData[$tmp['real_reviewer']])) {
                $tmp['real_reviewer'] = $userData[$tmp['real_reviewer']];
            }
            $reviewNode = $tmp;
        }
        $reviewRequest['nodes'] = $reviewRequestData;

        /** @var TaskModel $taskModel */
        $taskModel = model(TaskModel::class);
        $taskFields = [
            "task.id",
            "task.name",
            "task.code",
            'task.project_id',
            "step_category.id",
            "step_category.name",
            "step_category.code",
            "project.id",
            "project.name",
            "project.code",
            "entity.id",
            "entity.name",
            "step.id",
            "step.code",
            "step.name",
            "status.id",
            "status.code",
            "status.name",
            "status.correspond",
            "module.id",
            "module.code"
        ];

        $taskFields = build_complex_query_fields($taskFields);
        $taskModel->emptyComplexFilterRelatedModule();

        $taskData = $taskModel
            ->join("entity ON entity.id = task.entity_id", 'LEFT')
            ->join("module ON module.id = entity.module_id", "LEFT")
            ->join("step ON step.id = task.step_id", "LEFT")
            ->join("step_category ON step_category.id = task.step_category_id", "LEFT")
            ->join("project ON project.id = task.project_id", "LEFT")
            ->join("status ON status.id =  JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))", "LEFT")
            ->where(["task.id" => $versionData['version']['link_id']])
            ->field($taskFields)
            ->find();

        $taskData = separate_complex_query_data($taskData);
        return array_merge($versionData, ['review_request' => $reviewRequest, 'current_review_request_node' => $currentNode], $taskData);
    }


    /**
     * 生成版本号
     * @param $projectId
     * @param $moduleId
     * @param $linkId
     * @return int
     */
    public function calculateVersionNumber($projectId, $moduleId, $linkId)
    {
        $moduleCode = module_by_id($moduleId)['code'];

        $numberWhere = ['project_id' => $projectId, 'link_id' => $linkId, 'module_id' => $moduleId];
        if ($moduleCode == "task") {
            //判定是否是甲方还是乙方
            $partyATask = OrderService::getInstance()->getSourceTaskByTaskId($linkId);
            if (!empty($partyATask)) {
                $numberWhere = ['project_id' => $partyATask['project_id'], 'link_id' => $partyATask['id'], 'module_id' => $moduleId];
            }
        }
        $number = $this->versionModel->where($numberWhere)->max("number");
        if (empty($number)) {
            return 1;
        }
        return (int)$number + 1;
    }


    /**
     * 创建版本
     * @param $data
     * @return array
     * @throws Throwable
     */
    public function create($data, $mediaType = 'media')
    {
        if (!in_array($mediaType, ['media', 'cos_media'])) {
            throw_strack_exception('media type error', ErrorCode::MEDIA_ADD_FAILURE);
        }

        // version 创建 单任务同时只能一个进行创建
        $uniqueLogicKey = $data['version']['module_id'] . '_' . $data['version']['link_id'];
        $isTaskVersion = $data['version']['module_id'] == module_by_code('task')['id'];
        $mutex = new PHPRedisMutex([Redis::connection()->client()], "lock-version_create_$uniqueLogicKey");
        $res = $mutex->synchronized(function () use ($data, $isTaskVersion, $mediaType) {
            $res = [
                "version" => [],
                $mediaType => [],
                "message_hook_data" => [],
            ];
            $mediaService = MediaService::getInstance();
            $commonService = CommonService::instance('version');
            /** @var  VersionCheckListModel $versionCheckListModel */
            $versionCheckListModel = model(VersionCheckListModel::class);
            $reviewRequestService = ReviewRequestService::getInstance();
            $versionModuleData = Module::$moduleDictData['module_index_by_code']['version'];
            $versionData = $data['version'];
            $versionData['number'] = $this->calculateVersionNumber($versionData['project_id'], $versionData['module_id'], $versionData['link_id']);

            //媒体处理
            $data[$mediaType] = $data[$mediaType] ?? [];
            if ($mediaType == 'media') {

                foreach ($data[$mediaType] as &$mediaItem) {
                    $mediaService->generateMediaData($mediaItem);
                }
            }
            unset($mediaItem);
            //检查点处理
            $checkListData = null;
            if (isset($data['check_list']) && count($data['check_list']) > 0) {
                $checkListData = $data['check_list'];
                $this->checkCheckListData($versionData, $checkListData);
            }
            //审批流数据
            $reviewRequestData = $data['review_request'] ?? null;
            $latestNotClosedReviewRequest = null;
            if (!empty($reviewRequestData)) {
                // 检查上一个版本是否未审核
                $latestNotClosedReviewRequest = $this->checkLatestVersionReviewed($data['version']['link_id']);
            }

            $task = model(TaskModel::class)
                ->join('entity on entity.id = task.entity_id', 'left')
                ->where(['task.id' => $data['version']['link_id']])
                ->field('task.id,task.name,task.entity_id,task.executor,entity.project_id,entity.step_workflow_id,task.step_id,task.json')->find();

            $currentTaskJsonFields = json_decode((string)$task['json'], true);
            $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
            if (!empty($currentTaskJsonFields['task_status']) && $currentTaskJsonFields['task_status'] == $closedStatus['id']) {
                // 任务已取消
                throw new LogicException("task closed", ErrorCode::TASK_CLOSED);
            }

            $needNotifyFirstReviewNode = false;
            // 处理不需要审核的版本
            // 版本直接通过
            $versionData['is_pass'] = empty($reviewRequestData) ? 'yes' : 'no';
            if ($versionData['is_pass'] == 'yes') {
                $versionData['end_time'] = time();
            }
            $versionData['is_publish'] = 'no';
            if (!empty($checkListData)) {
                // 版本审核点直接通过
                $checkListData = array_map(function ($item) {
                    $item['is_pass'] = empty($reviewRequestData) ? 'yes' : 'no';
                    return $item;
                }, $checkListData);
            }
            $reviewRequestFinished = true;
            $this->versionModel->startTrans();
            try {
                // 关闭上次审核
                if (!empty($latestNotClosedReviewRequest)) {
                    ReviewRequestService::getInstance()->closeReviewRequest($latestNotClosedReviewRequest);
                }

                $mediaIds = [];
                foreach ($data[$mediaType] as $mediaItem) {
                    switch ($mediaType) {
                        default:
                        case "media":
                            $media = $this->mediaModel->addItem($mediaItem);
                            break;
                        case "cos_media":
                            $media = $this->cosMediaModel->addItem($mediaItem);
                            break;
                    }
                    if ($media === false) {
                        throw new \Exception($this->mediaModel->getError(), ErrorCode::MEDIA_ADD_FAILURE);
                    }
                    $mediaIds[] = $media['id'];
                    $res[$mediaType][] = $media;
                }

                //是否复制
                $isCopy = $data['is_copy'] ?? false;
                if (!$isCopy) {
                    $versionData["version_${mediaType}"] = implode(',', $mediaIds);
                }
                $versionData['param'] = $versionData['param'] ?? [];
                $version = $commonService->commonCreate(['data' => $versionData], module_by_code('version'));
                if ($version === false) {
                    throw new \Exception('Failed to create version', ErrorCode::VERSION_ADD_FAILURE);
                }

                $versionCheckList = null;
                //版本关联的工序检查点
                if (!empty($checkListData)) {
                    $versionCheckList = [];
                    foreach ($checkListData as $checkList) {
                        $checkList['version_id'] = $version['id'];
                        $tmpVersionCheckList = $versionCheckListModel->addItem($checkList);
                        if (!$tmpVersionCheckList) {
                            throw new \Exception("add version checkList error" . $versionCheckListModel->getError(), ErrorCode::VERSION_CHECKLIST_ADD_FAILURE);
                        }
                        $versionCheckList[] = $tmpVersionCheckList;
                    }
                    $res['version_check_list'] = $versionCheckList;
                }

                //审批数据
                $reviewRequest = [];
                if (!empty($reviewRequestData)) {
                    $reviewRequestData['data']['name'] = "版本" . $version['number'] . "审批";
                    $reviewRequestData['data']['link_id'] = $version['id'];
                    $reviewRequestData['data']['module_id'] = $versionModuleData['id'];
                    $reviewRequestData['data']['project_id'] = $version['project_id'];
                    //自动跳过填充param的数据
                    if (!empty($task) && !empty($versionCheckList)) {
                        $this->prepareReviewerCheckListData($task, $reviewRequestData['reviewers'], $versionCheckList);
                    }
                    $reviewRequest = $reviewRequestService->create($reviewRequestData);
                    $res['review_request'] = $reviewRequest;
                    $reviewRequestFinished = $reviewRequest['review_request']['is_pass'] === 'yes';
                    if ($reviewRequest['review_request']['is_pass'] !== "yes" && $isTaskVersion) {
                        // 任务版本审核
                        // 审核没有通过 那么通知第一个审核人
                        $needNotifyFirstReviewNode = true;
                    }
                }
                //处理版本创建后
                $this->afterVersionCreate($version, !empty($reviewRequestData), $reviewRequestFinished);
                $this->versionModel->commit();
                $res['version'] = $version;
                $hookDataGroup = [];
                if ($needNotifyFirstReviewNode && !empty($reviewRequest)) {
                    try {
                        if (!empty($task)) {
                            // 通知第一个任务审核节点
                            $hookDataGroup[] = [
                                'tag' => 'task_review',
                                'data' => [
                                    'type' => 'commit_version',
                                    'data' => [
                                        'version_id' => $version['id'],
                                        'review_request_id' => $reviewRequest['review_request']['id'],
                                    ],
                                ],
                            ];
                            $hookDataGroup[] = [
                                'tag' => 'task_review_created_after',
                                'data' => [
                                    'type' => 'task_review_active_node',
                                    'data' => [
                                        'task' => $task,
                                        'version_check_list' => $versionCheckList,
                                        'version' => $version,
                                        'created_by' => request()->getUserId(),
                                        'review_request' => $reviewRequest['review_request'],
                                        'nodes' => $reviewRequest['nodes'],
                                    ],
                                ],
                            ];
                        }
                    } catch (Throwable $e) {
                        // 通知异常拦截
                        trace(format_exception_info($e), 'ERR');
                    }
                }
                $res['message_hook_data'] = $hookDataGroup;
                return $res;
            } catch (Throwable $e) {
                $this->versionModel->rollback();
                throw $e;
            }
        });
        foreach ($res['message_hook_data'] as $hook) {
            if (empty($hook['tag'] ?? '') || empty($hook['data'] ?? [])) {
                continue;
            }
            Hook::listen($hook['tag'], $hook['data']);
        }
        unset($res['message_hook_data']);
        return $res;
    }

    /**
     * 在版本创建后
     * @param array $version
     * @param bool $needReview
     * @param bool $reviewRequestFinished
     * @throws Throwable
     */
    private function afterVersionCreate(array $version, bool $needReview, bool $reviewRequestFinished)
    {
        switch ($version['module_id']) {
            case module_by_code('task')['id']:
                $statusService = StatusService::getInstance();
                $waitPublishStatus = $statusService->getTaskReadyToPublishStatus();
                $version = model(VersionModel::class)->find($version['id']);
                if (!$needReview) {
                    // 不需要审核
                    // 需要处理外包提交甲方审核
                    $project = model(ProjectModel::class)->field('id,name,type')->find($version['project_id']);
                    $isOutsourceProject = $project['type'] === 'outsource';
                    if ($isOutsourceProject) {
                        APP_DEBUG && trace("发起甲方审核", "DEBUG");
                        $addedReviewData = $this->addPartyAReviewData($version);
                        if ($addedReviewData) {
                            // 修改任务状态为 待甲方审核
                            $status = $statusService->getTaskWaitForPartyAReviewStatus();
                            $this->versionModel->save(['id' => $version['id'], 'is_pass' => "no"]);
                        } else {
                            // 修改为待发布
                            $status = $waitPublishStatus;
                        }
                    } else {
                        $status = $waitPublishStatus;
                    }
                } else {
                    // 需要审核
                    if ($reviewRequestFinished) {
                        // 当前审核已经结束
                        // 状态控制 已经在review afterReviewFinished处理
                    } else {
                        // 当前审核没有结束 那么必定没有通过
                        if ($version['is_pass'] === "no") {
                            // 版本已经通过
                            $status = $statusService->getOne(['code' => "submitted"], 'id');
                        } else {
                            // 版本没有通过
                        }
                    }
                }
                if (!empty($status)) {
                    $data = [
                        'id' => $version['link_id']
                    ];
                    // 当前要更新成待发布 说明任务已经完成
                    if ($status['id'] == $waitPublishStatus['id']) {
                        $currentTask = model(TaskModel::class)->find($version['link_id']);
                        // 是概念任务 无需发布
                        if (
                            $currentTask['entity_module_id'] == module_by_code('design')['id']
                            ||
                            in_array($currentTask['code'], ['efx_asset', 'creatives_asset'])
                        ) {
                            model(VersionModel::class)->modifyItem(['id' => $version['id'], 'is_publish' => 'yes']);
                            $status = $statusService->getTaskDoneStatus();
                            // 追加交付时间字段
                            $data['delivery_time'] = time();
                        }
                        // 更新数据 追加任务完成时间字段
                        $data['end_time'] = time();
                    }
                    $data['task_status'] = $status['id'];
                    $data = CommonService::instance("task")->dealUpdateCustomField($data, 'task');
                    model(TaskModel::class)->modifyItem($data);
                }
                break;
        }
    }

    /**
     * 当版本审核完后
     * @param array $version
     * @param array $reviewRequest
     * @param bool $isPass
     * @throws Throwable
     */
    public function afterVersionReviewed(array $version, array $reviewRequest, bool $isPass)
    {
        $module = Module::$moduleDictData['module_index_by_id'][$version['module_id']];
        switch ($module['code']) {
            case "task":
                $commonService = CommonService::instance("task");
                $statusService = StatusService::getInstance();
                $waitPublishStatus = StatusService::getInstance()->getTaskReadyToPublishStatus();
                $doneStatus = StatusService::getInstance()->getTaskDoneStatus();

                // 更新版本审核点状态
                $versionCheckListArray = model(VersionCheckListModel::class)
                    ->where(['version_id' => $version['id']])
                    ->field("id")
                    ->select();
                if (count($versionCheckListArray) > 0) {
                    $versionCheckListUpdateData = ['is_pass' => $isPass ? 'yes' : 'no'];
                    $idList = array_column($versionCheckListArray, 'id');
                    model(VersionCheckListModel::class)->where(['id' => ['IN', implode(',', $idList)]])->save($versionCheckListUpdateData);
                }
                $currentTask = model(TaskModel::class)->find($version['link_id']);
                $project = model(ProjectModel::class)->field('id,name,type')->find($version['project_id']);
                $isOutsourceProject = $project['type'] === 'outsource';
                //更新审核状态
                if ($isPass) {
                    // 更新任务的处理状态
                    // 需要根据任务的类型 去更新不同的任务审核状态
                    $notSameTenantId = $reviewRequest['tenant_id'] !== $version['tenant_id'];
                    /**
                     * 检查任务属性
                     * 影子任务需要发送给甲方去审核
                     * 重置版本状态为待审核
                     */
                    if (!$notSameTenantId && $isOutsourceProject) {
                        /**
                         * 外包任务 当前在乙方进行审核 审核通过
                         * 提交给甲方审核
                         * 1 添加成功
                         * 1.1 修改版本为未通过
                         * 1.2 修改任务状态为待甲方审核
                         *
                         * 2 添加失败 不需要添加
                         * 2.1 修改任务为待发布状态
                         */
                        APP_DEBUG && trace("发起甲方审核", "DEBUG");
                        $addedReviewData = $this->addPartyAReviewData($version, $reviewRequest);
                        if ($addedReviewData) {
                            // 修改任务状态为 待甲方审核
                            $reviewedStatus = $statusService->getTaskWaitForPartyAReviewStatus();
                            $this->versionModel->save(['id' => $version['id'], 'is_pass' => "no"]);
                        } else {
                            // 修改为待发布
                            $reviewedStatus = $waitPublishStatus;
                        }
                        if (empty($reviewedStatus)) {
                            throw new LogicException("missing status config", ErrorCode::MISSING_CONFIG);
                        }
                    } else {
                        /**
                         * 在甲方进行审核通过
                         * 1. 检查是否还有审核点
                         * 2. 修改任务状态(无待审核审核点 进入待发布 有审核节点 进入任务进行中状态)
                         */
                        // 检查是否是概念任务
                        if (
                            $currentTask['entity_module_id'] == module_by_code('design')['id']
                            ||
                            in_array($currentTask['code'], ['efx_asset', 'creatives_asset'])
                        ) {
                            // 概念任务无需发布 直接修改为已发布
                            model(VersionModel::class)->modifyItem(['id' => $version['id'], 'is_publish' => 'yes']);
                            // 审核通过  就进入完成状态
                            $reviewedStatus = $doneStatus;
                        } else {
                            // 审核通过 就进入待发布状态
                            $reviewedStatus = $waitPublishStatus;
                        }
                        if (empty($reviewedStatus)) {
                            throw new LogicException("missing task review progress status", ErrorCode::MISSING_TASK_REVIEW_PROGRESS_STATUS);
                        }
                    }
                } else {
                    //审核未通过
                    APP_DEBUG && trace("任务审核未通过", "DEBUG");
                    $reviewedStatus = $statusService->getTaskReviewRejectStatus();
                    if (empty($reviewedStatus)) {
                        throw new LogicException("missing task review progress status", ErrorCode::MISSING_TASK_REVIEW_PROGRESS_STATUS);
                    }
                }
                $taskUpdateData = [
                    'id' => $version['link_id'],
                    'task_status' => $reviewedStatus['id']
                ];
                // 审核通过修改为待发布/完成状态 同时修改任务的完成时间为当前时间
                if (in_array($reviewedStatus['id'], [$doneStatus['id'], $waitPublishStatus['id']])) {
                    $taskUpdateData['end_time'] = time();
                }
                $commonService->setCurrentModuleCode("task");
                $commonService->update($taskUpdateData, 'task', false);
                try {
                    $hookData = [
                        'type' => 'result',
                        'data' => [
                            'tenant_id' => request()->getTenantId(),
                            'version_id' => $version['id'],
                            'review_request_id' => $reviewRequest['id'],
                            'is_pass' => $isPass,
                        ],
                    ];
                    Hook::listen('task_review', $hookData);
                    $hookData = [
                        'type' => 'task_review_result',
                        'data' => [
                            'version' => $version,
                            'review_request' => $reviewRequest,
                            'is_pass' => $isPass,
                        ],
                    ];
                    Hook::listen('task_review_finished_after', $hookData);
                } catch (Throwable $e) {
                    trace(format_exception_info($e), 'ERR');
                }
                break;
        }
    }

    /**
     * 任务版本列表
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function selectTaskVersions($param)
    {
        $taskModule = Module::$moduleDictData['module_index_by_code']['task'];
        $param['filter'] = append_filter_param($param, 'version', ['module_id' => $taskModule['id']]);
        $returnData = $this->versionModel->forceMasterDB()->selectData($param);
        if (empty($returnData['rows'])) {
            return [];
        }

        $linkIds = [];
        $versionIds = [];
        foreach ($returnData['rows'] as $value) {
            if (!empty($value['version']['id'])) {
                $versionIds[] = $value['version']['id'];
            }
            if (!empty($value['version']['link_id'])) {
                $linkIds[] = $value['version']['link_id'];
            }
        }

        $statusList = model(StatusModel::class)->select();
        $statusMap = array_column($statusList, null, 'id');

        $reviewDataMap = []; // 增加审核人信息
        if (!empty($versionIds)) {
            $versionModuleData = Module::$moduleDictData['module_index_by_code']['version'];
            $reviewData = model(ReviewRequestModel::class)
                ->join("review_request_node on review_request.id = review_request_node.review_request_id")
                ->join("user on user.id = review_request_node.plan_reviewer")
                ->where(['review_request.link_id' => ['IN', $versionIds]])
                ->where(['review_request.module_id' => $versionModuleData['id']])
                ->where(['review_request_node.is_active' => 'yes'])
                ->field('review_request_node.plan_reviewer,review_request.link_id,user.id as user_id,user.name as user_name')
                ->select();
            $reviewDataMap = array_column($reviewData, null, 'link_id');
        }

        $taskMap = [];  // 增加任务信息
        if (!empty($linkIds)) {
            $taskData = model(TaskModel::class)->join("entity on task.entity_id = entity.id")
                ->where(['task.id' => ['IN', $linkIds]])
                ->field("task.id,task.plan_start_time,task.plan_end_time,task.json ->> '$.task_status' as task_status,entity.initial_episode_code,entity.showings_number,entity.name")
                ->select();
            $taskMap = array_column($taskData, null, 'id');
        }
        $returnData['rows'] = array_map(function ($tmp) use ($reviewDataMap, $taskMap, $statusMap) {
            if (!empty($tmp['version']['id'])) {
                $tmp['plan_reviewer'] = $reviewDataMap[$tmp['version']['id']] ?? 0;
            }
            if (!empty($tmp['version']['link_id'])) {
                $tmp['task_info'] = $taskMap[$tmp['version']['link_id']] ?? null;
                if (!empty($tmp['task_info']['task_status'])) {
                    $tmp['task_info']['status'] = $statusMap[$tmp['task_info']['task_status']] ?? null;
                }
            }
            return $tmp;
        }, $returnData['rows']);
        return $returnData;
    }

    /**
     * 添加影子任务审核信息
     * @param $version
     * @param $reviewRequest
     * @return bool
     * @throws Throwable
     * @throws Exception
     */
    private function addPartyAReviewData($version, $reviewRequest = null)
    {
        $order = OrderService::getInstance()->getOrderByStepTaskId($version['link_id']);
        if (empty($order)) {
            throw new LogicException("missing order data", ErrorCode::MISSING_ORDER_DATA);
        }
        $workflowReviewData = OutsideTaskReviewWorkflowService::getInstance()->selectReview($version['link_id'], $order['id']);
        if (empty($workflowReviewData)) {
            return false;
        }
        $reviewNodeList = $workflowReviewData['matched_data']['reviewers'];
        $reviewers = [];
        foreach ($reviewNodeList as $nodeData) {
            $firstReviewer = array_shift($nodeData['user_info']);
            $reviewers[] = [
                'role_code' => $nodeData['role_code'],
                'user_id' => $firstReviewer['id'],
                'skip' => 0,
            ];
        }
        if (!empty($reviewRequest)) {
            $order['parent_review_request_id'] = $reviewRequest['id'];
        }
        $reviewData = [
            'reviewers' => $reviewers,
            'data' => [
                'review_workflow_id' => $workflowReviewData['review_workflow']['id'],
                'name' => "订单影子任务审核{$order['name']}-{$order['number']}",
                'param' => $order,
                'link_id' => $version['id'],
                'module_id' => module_by_code('version')['id'],
                'tenant_id' => $order['from_tenant_id'],
                'project_id' => $order['project_id'],
            ]
        ];
        $res = ReviewRequestService::getInstance()->create($reviewData);

        APP_DEBUG && trace("Add review request {$reviewData['data']['name']} to party a", "DEBUG");

        // 通知甲方第一个人审核
        try {
            $reviewRequest = $res['review_request'];
            $nodes = $res['nodes'];
            $hookData = [
                'type' => 'task_review_active_node_outsource',
                'data' => [
                    'version' => $version,
                    'review_request' => $reviewRequest,
                    'nodes' => $nodes,
                ]
            ];
            Hook::listen('task_review_created_after', $hookData);

            $reviewMan = 0;
            foreach ($nodes as $node) {
                if ($node['is_pass'] = "no" && $node['is_reviewed'] == "no" && $node['is_active'] == "yes") {
                    $reviewMan = $node['plan_reviewer'];
                    break;
                }
            }
            if ($reviewMan > 0) {
                $hookData = [
                    'type' => 'commit_version',
                    'data' => [
                        'version_id' => $version['id'],
                        'review_request_id' => $reviewRequest['id'],
                        'receiver' => $reviewMan
                    ],
                ];
                Hook::listen('task_review', $hookData);
            }
        } catch (Throwable $e) {
            trace(format_exception_info($e), 'ERR');
        }
        return true;
    }

    /**
     * 获得任务待审核通知模板id
     * @return int
     */
    public function getTaskReviewNotifyTemplateId()
    {
        $messageTemplateId = OptionsService::getInstance()->getOptionsConfigItemData('message_notify_template_config', 'task_review_notify_template_id');
        return (int)$messageTemplateId;
    }

    /**
     * 任务审核完毕通知
     * @param array $version
     * @param array $reviewRequest
     * @param bool $isPass
     * @return void
     * @throws \Exception
     */
    private function notifyTaskVersionReviewed(array $version, array $reviewRequest, bool $isPass)
    {
        /**
         * 审核完毕
         *
         * 审核未通过 需要通知之前通过的所有人和提交人
         *
         * 审核通过 需要通知提交人
         *
         * 外部任务审核 需要查询关联的外部任务 以及外部任务审核关系
         *
         */

        $notifyMessages = [];
        $task = model(TaskModel::class)->find($version['link_id']);
        $entity = model(EntityModel::class)->find($task['entity_id']);

        $taskService = TaskService::getInstance();
        $parentTask = $taskService->getParentTask($task['id'], $task['project_id']);
        $tenantId = request()->getTenantId();

        $nodes = model(ReviewRequestNodeModel::class)
            ->where([
                'review_request_id' => $reviewRequest['id'],
                'is_reviewed' => "yes",
            ])
            ->order("id ASC")
            ->select();
        $lastReviewerNode = $nodes[count($nodes) - 1];

        $reviewerIdArray = array_column($nodes, 'plan_reviewer', 'plan_reviewer');
        $userIDArray = array_merge($reviewerIdArray, [$task['executor']]);
        $userIDArray = array_unique($userIDArray);

        $users = model(UserModel::class)->where(['id' => ['IN', implode(',', $userIDArray)]])->select();
        $usersMap = array_column($users, null, 'id');
        $templateId = $this->getTaskReviewedMessageTemplateId();
        $executor = $usersMap[$task['executor']] ?? ['name' => 'None', 'union_id' => 0];
        $lastReviewer = $usersMap[$lastReviewerNode['plan_reviewer']] ?? ['name' => 'None'];

        $notifyParam = [
            'first' => '任务审核已完成',
            'task_name' => $entity['name'] . "-" . $task['name'],
            'executor_name' => $executor['name'],
            'reviewer_name' => $lastReviewer['name'],
            'review_at' => date("Y/m/d H:i", $lastReviewerNode['reviewed']),
            'review_result' => $isPass ? "通过" : "未通过",
            'remark' => $isPass ? '请进行下一任务' : '请修改后重新提交审核',
            'task_id' => $task['id'],
            'entity_id' => $task['entity_id'],
        ];

        if ($parentTask && $parentTask['type'] == "shadow" && $tenantId != $parentTask['tenant_id']) {
            /**
             * 当前审核 甲方审核  审核的是外部任务
             *
             */
            //审核外部任务
            //查询外部审核请求数据 通知审核人

            $reviewRequestParam = json_decode($reviewRequest['param'], true);
            $partyBReviewRequestId = $reviewRequestParam['parent_review_request_id'] ?? 0;

            /**
             * 通知乙方审核流的审核人
             */
            if ($partyBReviewRequestId) {
                $partyBReviewRequestNodes = model(ReviewRequestNodeModel::class)
                    ->where([
                        'review_request_id' => $partyBReviewRequestId,
                        'is_reviewed' => "yes",
                        'plan_reviewer' => ['GT', 0],
                    ])
                    ->order("id ASC")
                    ->select();
                $partyBUsers = array_column($partyBReviewRequestNodes, 'plan_reviewer', 'plan_reviewer');
                $userIDArray = array_merge($userIDArray, $partyBUsers);
                $userIDArray = array_unique($userIDArray);

                $users = model(UserModel::class)->where(['id' => ['IN', implode(',', $userIDArray)]])->select();
                $usersMap = array_column($users, null, 'id');

                foreach ($partyBReviewRequestNodes as $node) {
                    if (empty($usersMap[$node['plan_reviewer']]['union_id'])) {
                        continue;
                    }

                    $notifyParam['first'] = '任务审核已完成';
                    $notifyParam['remark'] = $isPass ? '请进行下一任务' : "请修改后重新提交审核";
                    $notifyMessages[] = [
                        'param' => $notifyParam,
                        'receiver' => $usersMap[$node['plan_reviewer']]['union_id'],
                        'template_id' => $templateId
                    ];
                }
            }
            /**
             *
             * 当审核不通过的时候
             *
             * 通知甲方审核人
             *
             */
            if (!$isPass) {
                $tenant = teamones_request('teamones-im', 'tenant/find', ['param' => [
                    'filter' => [
                        'id' => $parentTask['tenant_id'],
                    ],
                ],
                ])['data'];
                foreach ($nodes as $node) {
                    if ($node['id'] == $lastReviewerNode['id']) {
                        continue;
                    }
                    if (empty($usersMap[$task['executor']]['union_id'])) {
                        continue;
                    }
                    $notifyParam['first'] = '与导演意见不一致';
                    $notifyParam['remark'] = '请进行交流';
                    $notifyParam['executor_name'] = $tenant['name'] ?? 'unknown company name';

                    $notifyMessages[] = [
                        'param' => $notifyParam,
                        'receiver' => $usersMap[$task['executor']]['union_id'],
                        'template_id' => $templateId
                    ];
                }
            }
        } else {
            // 审核内部任务
            $notifyParam['first'] = '任务审核已完成';
            $notifyParam['remark'] = $isPass ? '请进行下一任务' : '请修改后重新提交审核';
            $notifyMessages[] = [
                'param' => $notifyParam,
                'receiver' => $executor['union_id'],
                'template_id' => $templateId
            ];
            // 通过
            if (!$isPass) {
                // 未通过
                foreach ($nodes as $node) {
                    if ($node['id'] == $lastReviewerNode['id']) {
                        continue;
                    }
                    if (empty($usersMap[$node['plan_reviewer']]['union_id'])) {
                        continue;
                    }
                    $notifyMessages[] = [
                        'param' => $notifyParam,
                        'receiver' => $usersMap[$node['plan_reviewer']]['union_id'],
                        'template_id' => $templateId
                    ];
                }
            }
        }

        foreach ($notifyMessages as $message) {
            Client::send('send_notify_message_v2', build_queue_data_with_xu([
                "param" => $message['param'],
                'receiver' => $message['receiver'],
                'template_id' => $message['template_id'],
                'content_type' => 'card_message',
                'notice_type' => 'examine',
                'is_popup' => 1,
                'message_key' => 'task_version_review_result',
            ]));
        }
    }

    /**
     * 任务审核通知模板id
     * @return int
     */
    public function getTaskReviewedMessageTemplateId()
    {
        $messageTemplateId = OptionsService::getInstance()->getOptionsConfigItemData("message_notify_template_config", "task_reviewed_message_template_id");
        return (int)$messageTemplateId;
    }

    /**
     * 通知甲方第一个审核人
     * @param $version
     * @param $reviewRequest
     * @param $nodes
     * @return void
     * @throws \Exception
     */
    private function notifyPartyAFirstReviewer($version, $reviewRequest, $nodes)
    {
        $reviewMan = 0;
        $nodeId = 0;
        foreach ($nodes as $node) {
            if ($node['is_pass'] = "no" && $node['is_reviewed'] == "no" && $node['is_active'] == "yes") {
                $reviewMan = $node['plan_reviewer'];
                $nodeId = $node['id'];
                break;
            }
        }
        if ($reviewMan <= 0) {
            return null;
        }
        $hookData = [
            'type' => 'commit_version',
            'data' => [
                'version_id' => $version['id'],
                'review_request_id' => $reviewRequest['id'],
                'receiver' => $reviewMan
            ],
        ];
        Hook::listen('task_review', $hookData);
        $task = model(TaskModel::class)->find($version['link_id']);
        $tenant = teamones_request('teamones-im', 'tenant/find', ['param' => [
            'filter' => [
                'id' => $task['tenant_id'],
            ],
        ]])['data'];

        $versionCheckList = model(VersionCheckListModel::class)->where(['version_id' => $version['id']])->find();
        $entity = model(EntityModel::class)->field('name')->find($task['entity_id']);
        $messageParam = [
            'first' => '任务已提交',
            'supplier_name' => $tenant['name'],
            'task_name' => $entity['name'] . "-" . $task['name'],
            'create_at' => date("Y/m/d H:i"),
            'remark' => '请及时审核',
            'version_id' => $versionCheckList['version_id'],
            'review_request_id' => $reviewRequest['id'],
            'task_id' => $task['id'],
            'review_request_node_id' => $nodeId,
        ];
        $messageTemplateId = $this->getShadowTaskReviewNotifyTemplateId();

        $reviewMan = model(UserModel::class)->field('union_id')->find($reviewMan);
        Client::send('send_notify_message_v2', build_queue_data_with_xu([
            "param" => $messageParam,
            'receiver' => $reviewMan['union_id'],
            'template_id' => $messageTemplateId,
            'content_type' => 'card_message',
            'notice_type' => 'message_review',
            'is_popup' => 1,
            'message_key' => 'task_version_need_review',
        ]));
    }

    /**
     * 获得外部任务审核通知模板
     * @return int
     */
    public function getShadowTaskReviewNotifyTemplateId()
    {
        $messageTemplateId = OptionsService::getInstance()->getOptionsConfigItemData('message_notify_template_config', 'shadow_task_review_notify_template_id');
        return (int)$messageTemplateId;
    }

    /**
     * 版本文件列表
     * @param $param
     * @param $moduleCode
     * @return array
     * @throws Exception
     */
    public function selectVersionMediaList($param, $moduleCode)
    {

        $fields = "version.id,version.link_id,version.project_id,version.description,media.id,media.thumb,media.type,media.param,user.id,user.avatar,user.name,user.union_id";

        $param['fields'] = $fields;
        $commonService = CommonService::instance($moduleCode);
        $res = $commonService->select($param);

        $taskIdArray = [];
        foreach ($res['rows'] as &$row) {
            $row['project_id'] = $row['version']['project_id'];
            $row['task_id'] = $row['version']['link_id'];
            $taskIdArray[$row['task_id']] = $row['task_id'];

        }

        $tasks = model(ProjectModel::class)
            ->join("task ON task.project_id = project.id")
            ->join("entity ON entity.id = task.entity_id")
            ->field("task.id , concat(project.name,'-',entity.name,'-',task.name) as name")
            ->where([
                'task.id' => ['in', implode(',', $taskIdArray)],
            ])->select();

        $tasks = array_column($tasks, "name", 'id');

        $res['rows'] = array_map(function ($item) use ($tasks) {
            $item['name'] = $tasks[$item['task_id']] ?? $item['description'];
            return $item;
        }, $res['rows']);

        $realRows = [];
        foreach ($res['rows'] as &$row) {
            if (count($row['media']) > 1) {
                foreach ($row['media'] as $media) {
                    if (!in_array($media['type'], ['video', 'image'])) {
                        continue;
                    }
                    $realRows[] = array_merge($row, ['media' => $media]);
                }

            } else {
                $row['media'] = array_shift($row['media']);
                if (!in_array($row['media']['type'], ['video', 'image'])) {
                    continue;
                }
                $realRows[] = $row;
            }
        }
        $res['rows'] = $realRows;

        return $res;
    }

    /**
     * 查询任务最后一个版本的提交 审核未通过信息
     * @param $param
     * @param $tenantId
     * @return array|null
     */
    public function getTaskLatestVersionFailureReviewResult($param, $tenantId)
    {
        $taskId = $param['task_id'];

        $latestVersion = model(VersionModel::class)
            ->where([
                'link_id' => $taskId,
                'module_id' => Module::$moduleDictData['module_index_by_code']['task']['id'],
            ])
            ->order("id desc")
            ->find();
        if (empty($latestVersion)) {
            return null;
        }
        $reviewRequest = model(ReviewRequestModel::class)
            ->where([
                'link_id' => $latestVersion['id'],
                'module_id' => Module::$moduleDictData['module_index_by_code']['version']['id'],
                'tenant_id' => $tenantId,
                'is_reviewed' => 'yes',
                'is_pass' => 'no',
            ])
            ->find();

        if (empty($reviewRequest)) {
            return null;
        }

        // 查找出最后一个未通过的审核人
        $reviewRequestNode = model(ReviewRequestNodeModel::class)
            ->where([
                'review_request_id' => $reviewRequest['id'],
                'is_reviewed' => 'yes',
                'is_pass' => 'no',
            ])
            ->order("id desc")
            ->find();

        if (empty($reviewRequestNode)) {
            return null;
        }

        $reviewerUserId = $reviewRequestNode['real_reviewer'] > 0 ? $reviewRequestNode['real_reviewer'] : $reviewRequestNode['plan_reviewer'];
        if ($reviewerUserId <= 0) {
            return null;
        }

        $reviewUser = model(UserModel::class)->field("json", true)->find($reviewerUserId);
        if (empty($reviewUser)) {
            return null;
        }

        return [
            'review_request' => $reviewRequest,
            'task' => model(TaskModel::class)->find($taskId),
            'review_requestNode' => $reviewRequestNode,
            'review_user' => $reviewUser,
            'version' => $latestVersion,
        ];
    }

    /**
     * 检查提交的审核点是否正确
     * @param $versionData
     * @param $checkListData
     * @throws Exception
     */
    private function checkCheckListData($versionData, $checkListData)
    {
        $moduleId = $versionData['module_id'];
        if ($moduleId !== Module::$moduleDictData['module_index_by_code']['task']['id']) {
            return;
        }
        $taskService = TaskService::getInstance();

        $allCheckList = $taskService->getMarkedCheckList([
            'param' => [
                'filter' => [
                    'task_id' => $versionData['link_id']
                ]
            ]
        ]);
        $allCheckList = array_column($allCheckList, 'id');
        foreach ($checkListData as $checkListItem) {
            if (!in_array($checkListItem['check_list_id'], $allCheckList)) {
                throw_strack_exception('invalid check_list data', ErrorCode::INVALID_CHECK_LIST_DATA);
            }
        }

    }

    /**
     *  获取动态数据
     * 1、项目级别
     * 2、团队级别
     * 3、实体级别
     * 3、自定义过滤条件（ORK组）
     *
     * @param array $filter
     * @param array $page
     * @param array $taskDataMap
     * @return array
     */
    public function getActivityData(array $filter, array $page = [1, 10], array $taskDataMap = [])
    {

        $versionFields = [
            "version.id",
            "version.number",
            "version.link_id",
            "version.created",
            "version.is_pass",
            "version.is_publish",
            "version.published",
            "version.json",
            "user.id",
            "user.name",
            "user.email",
            "user.avatar",
            "user.phone",
            "user.union_id",
        ];

        $pageNumber = !empty($page[0]) ? $page[0] : 1;
        $pageSize = !empty($page[1]) ? $page[1] : 10;

        $versionData = $this->versionModel
            ->join("user user ON user.id = version.created_by", "LEFT")
            ->where($filter)
            ->field(build_complex_query_fields($versionFields, "__"))
            ->order('version.created desc')
            ->page($pageNumber, $pageSize)
            ->select();

        if (!empty($versionData)) {
            // 查询媒体资源
            $mediaIds = [];
            $versionMediaMap = [];
            $newVersionData = [];
            foreach ($versionData as $versionItem) {
                $versionItem['version__json'] = json_decode($versionItem['version__json'], true);
                if (isset($versionItem['version__json']['version_media'])) {
                    $versionMediaIds = explode(',', $versionItem['version__json']['version_media']);
                    $versionMediaMap[$versionItem['version__id']] = $versionMediaIds;
                    foreach ($versionMediaIds as $versionMediaId) {
                        if (!in_array($versionMediaId, $mediaIds)) {
                            $mediaIds[] = $versionMediaId;
                        }
                    }
                } else {
                    $versionMediaMap[$versionItem['version__id']] = [];
                }

                $versionNewItem = separate_complex_query_data($versionItem);
                $versionNewItem['media_data'] = [];

                $versionNewItem['task'] = !empty($taskDataMap[$versionItem['version__link_id']]) ? $taskDataMap[$versionItem['version__link_id']] : "";

                $newVersionData[] = $versionNewItem;
            }

            unset($versionData);

            $mediaModel = model(MediaModel::class);
            $versionMediaData = $mediaModel->where(['id' => ['IN', join(',', $mediaIds)]])
                ->field("id,thumb,type,param")
                ->select();

            if (!empty($versionMediaData)) {
                $versionMediaFormatData = array_map(function ($mediaItem) {
                    return CommonService::getFormatAttrData(model(MediaModel::class), $mediaItem);
                }, $versionMediaData);

                $versionMediaFormatMapData = array_column($versionMediaFormatData, null, 'id');
                foreach ($newVersionData as &$versionItem) {
                    if (!empty($versionMediaMap[$versionItem['version']['id']])) {
                        foreach ($versionMediaMap[$versionItem['version']['id']] as $versionId) {
                            if (!empty($versionMediaFormatMapData[$versionId])) {
                                $versionItem['media_data'][] = $versionMediaFormatMapData[$versionId];
                            }
                        }
                    }
                }
            }

            return $newVersionData;
        }

        return [];
    }

    /**
     * 获取实体级别动态数据列表
     * @param $taskId
     * @param array $page
     * @param int $stepId
     * @return array
     */
    public function getTaskEntityLevelActivity($taskId, $page = [], $stepId = 0)
    {
        $taskModel = model(TaskModel::class);
        $taskData = $taskModel->field('entity_id,entity_module_id')->where(['id' => $taskId])->find();
        if (!empty($taskData) && !empty($taskData['entity_id']) && !empty($taskData['entity_module_id'])) {

            // 查找同一个是一个实体的任务数据
            $filter = [
                'task.entity_id' => $taskData['entity_id'],
                'task.entity_module_id' => $taskData['entity_module_id'],
                'task.step_type' => 'step'
            ];

            $entityService = EntityService::getInstance();
            $relationEntities = $entityService->getRelatedEntities($taskData['entity_id']);

            if (count($relationEntities) > 1) {
                $filter['task.entity_id'] = ['in', implode(',', array_column($relationEntities, 'id'))];
                $filter['task.entity_module_id'] = ['in', implode(',', array_column($relationEntities, 'module_id'))];
            }


            if ($stepId > 0) {
                $filter['task.step_id'] = $stepId;
            }

            $taskIdsData = $taskModel
                ->alias('task')
                ->join('step step ON step.id = task.step_id', "LEFT")
                ->join('user assignee ON assignee.id = task.assignee', "LEFT")
                ->join('user executor ON executor.id = task.executor', "LEFT")
                ->field('task.id,task.name,task.step_id,step.name as step_name,executor.name as executor_name,assignee.name as assignee_name')
                ->where($filter)
                ->select();

            if (!empty($taskIdsData)) {
                $stepNameMap = array_column($taskIdsData, null, 'id');
                return $this->getActivityData([
                    'version.link_id' => ['IN', join(',', array_column($taskIdsData, 'id'))],
                    'version.module_id' => Module::$moduleDictData['module_index_by_code']['task']['id'],
                    'version.is_publish' => 'yes'
                ], $page, $stepNameMap);
            }

            return [];
        } else {
            throw_strack_exception('task not exist', ErrorCode::TASK_NOT_EXIST);
        }
    }

    /**
     * 查询任务关联动态工序筛选列表
     * @param $id
     * @return array|false|mixed|string
     */
    public function getTaskEntityActivityStepFilter($id)
    {
        $entityService = EntityService::getInstance();
        $relatedEntities = $entityService->getRelatedEntities($id);
        $res = [];
        if (count($relatedEntities)) {
            $projectId = $relatedEntities[0]['project_id'];
            $entityIdList = array_column($relatedEntities, 'id');
            $taskStepIdList = model(TaskModel::class)
                ->where([
                    'project_id' => $projectId,
                    'step_type' => 'step',
                    'entity_id' => ['IN', implode(',', $entityIdList)],
                ])
                ->field('step_id')
                ->group('step_id')
                ->select();

            $stepIdList = array_column($taskStepIdList, 'step_id');
            $res = model(StepModel::class)->where(['id' => ['IN', implode(',', $stepIdList)]])->order('id ASC')->select();
        }
        return $res;
    }

    /**
     * 查询任务最新的版本
     * @param int $taskId
     * @return array
     * @throws Exception
     */
    public function getLatestVersion(int $taskId)
    {
        $versionFilter = [
            'module_id' => module_by_code('task')['id'],
            'link_id' => $taskId,
            'is_deprecate' => 'no',
        ];
        return $this->versionModel->where($versionFilter)->order('number DESC')->find();
    }

    /**
     * 检查最后一个版本是否审核过
     * @param int $taskId 任务id
     * @return null|array
     * @throws Exception
     */
    public function checkLatestVersionReviewed(int $taskId)
    {
        $latestVersion = $this->getLatestVersion($taskId);
        if (empty($latestVersion)) {
            // 没有最后一个版本
            return null;
        }
        $reviewRequest = model(ReviewRequestModel::class)
            ->where(['module_id' => module_by_code('version')['id'], 'link_id' => $latestVersion['id']])
            ->order('id desc')
            ->find();
        if (!empty($reviewRequest) && $reviewRequest['is_reviewed'] === 'no') {
            // 结束审核
            return $reviewRequest;
        }
        return null;
    }

    /**
     * 获取任务的最大版本的信息
     * @param $taskIds
     * @param string $field
     * @param bool $versionOnlyPublished
     * @return array
     */
    public function getTaskMaxVersionInfo($taskIds, $field = "*", $versionOnlyPublished = false)
    {
        if (empty($taskIds)) {
            return [];
        }
        $mvq = model(VersionModel::class)
            ->forceMasterDB()
            ->where(['link_id' => ['IN', $taskIds], 'module_id' => module_by_code('task')['id']])
            ->field('max(id) as id')
            ->group('link_id');
        if ($versionOnlyPublished) {
            $mvq = $mvq->where(['is_publish' => 'yes']);
        }
        //版本查询
        $maxVersionId = $mvq->column('id');
        if (empty($maxVersionId)) {
            return [];
        }
        return model(VersionModel::class)
            ->forceMasterDB()
            ->where(['id' => ['IN', $maxVersionId]])
            ->field($field)->select();
    }

    /**
     * 查询多个任务最新的版本
     * @param array $param
     * @return array|false|mixed|string
     */
    public function getManyLatestVersion(array $param)
    {
        $query = $this->versionModel;
        if (array_key_exists("fields", $param)) {
            $query->field($param['fields']);
        }
        if (array_key_exists("filter", $param)) {
            $query->where($param['filter']);
        }
        if (array_key_exists("group", $param)) {
            $query->group($param['group']);
        }
        $versionIds = $query->column('id');
        if (empty($versionIds)) {
            return [];
        }
        return $this->versionModel->forceMasterDB()->where(['id' => ['IN', $versionIds]])->select();
    }

    /**
     * 获取最新审核节点审核人是我的任务信息
     * @param $userId
     * @param $tenantId
     * @param $projectIds
     * @param $filter
     * @return array|mixed|string
     */
    public function getMeExecutorReviewInfo($userId, $tenantId, $projectIds, &$filter)
    {
        $projectData = ProjectService::getInstance()->getProjects(['belong_project_id' => ['IN', $projectIds]], 'id,belong_project_id');
        if (!empty($projectData)) {
            foreach ($projectData as $item) {
                $projectIds[] = $item['id'];
            }
            $filter['task.project_id'] = ['IN', $projectIds];
        }
        $fields = [
            "review_request.id",
            "review_request.name",
            "review_request.created",
            "review_request_node.id",
            "review_request_node.is_pass",
            "review_request_node.is_reviewed",
            "review_request_node.reviewed",
            "review_request_node.start_at",
            "review_request.link_id",
            "version.link_id"
        ];
        $versionModuleData = Module::$moduleDictData['module_index_by_code']['version'];
        $reviewData = model(ReviewRequestModel::class)
            ->join("review_request_node on review_request.id = review_request_node.review_request_id")
            ->join("version on version.id = review_request.link_id")
            ->where(['review_request.module_id' => $versionModuleData['id']])
            ->where(['review_request_node.is_active' => 'yes', 'review_request.tenant_id' => $tenantId])
            ->where(['review_request_node.plan_reviewer' => $userId])
            ->where(['version.project_id' => ['IN', $projectIds]])
            ->field($fields)
            ->select();
        if (empty($reviewData)) {
            return [];
        }
        return $reviewData;
    }

    /**
     * 查询任务最新版本列表
     * @param $upstreamTaskIdList
     * @param array $extraFilter
     * @param null $fields
     * @return array|false|mixed|string
     */
    public function getMaxTaskListVersionList($upstreamTaskIdList, $extraFilter = [], $fields = null)
    {
        $versionFilter = [
            'link_id' => ['IN', implode(',', $upstreamTaskIdList)],
            'module_id' => Module::$moduleDictData['module_index_by_code']['task']['id']
        ];
        if (!empty($extraFilter)) {
            $versionFilter[] = $extraFilter;
        }
        // 查询所有上游任务的最新版本
        $maxVersionIdList = model(VersionModel::class)->forceMasterDB()
            ->where($versionFilter)
            ->group("link_id")
            ->field("max(id) as id")
            ->select();


        $maxVersionIdList = array_column($maxVersionIdList, 'id');

        if (empty($maxVersionIdList)) {
            return [];
        }

        if (!isset($fields)) {
            $fields = 'id,link_id,link_id as task_id,storage_type,storage_id,pack_name,pack_pass,tenant_id,id as version_id,cos_upload_name,updated,is_pass,is_publish';
        }


        // 查询文件存储信息
        return model(VersionModel::class)->forceMasterDB()
            ->where([
                'id' => ['IN', implode(',', $maxVersionIdList)],
            ])
            ->field($fields)
            ->select();
    }

    /**
     * 处理审核节点版本审核点审核状态信息
     * @param $task
     * @param $reviewers
     * @param array $versionCheckList
     * @return void
     */
    private function prepareReviewerCheckListData($task, &$reviewers, array $versionCheckList)
    {
        $insideReviewWorkflowService = InsideTaskReviewWorkflowService::getInstance();
        $checkListConfig = $insideReviewWorkflowService->getTaskCheckListConfig($task['id'], $task['project_id'], $task['step_workflow_id'], $task['step_id']);
        $checkListConfig = array_column($checkListConfig, null, 'id');
        $versionCheckListMap = array_column($versionCheckList, null, 'check_list_id');
        foreach ($reviewers as &$reviewer) {
            //判断任务的执行人是否是组长,自动跳过
            if ($reviewer['role_code'] == TaskService::HEADMAN_CODE) {
                if ($task['executor'] == $reviewer['user_id']) {
                    $reviewer['skip'] = 1;
                }
            }
            if ($reviewer['skip']) {
                $tmpPassCheckListData = [];
                //比较出交集
                if (!empty($checkListConfig)) {
                    $canSeeVersionCheckList = [];
                    foreach ($versionCheckListMap as $checkListId => $versionCheckListItem) {
                        if ((isset($checkListConfig[$checkListId]) && in_array($reviewer['role_code'], $checkListConfig[$checkListId]['review_role_config']))) {
                            $canSeeVersionCheckList[] = $versionCheckListItem;
                        }
                    }
                } else {
                    $canSeeVersionCheckList = array_values($versionCheckListMap);
                }
                foreach ($canSeeVersionCheckList as $canSeeVersionCheckListItem) {
                    $tmpPassCheckListData[] = [
                        'is_pass' => "yes",
                        'is_reviewed' => "yes",
                        'check_list_id' => $canSeeVersionCheckListItem['check_list_id'],
                        'version_check_list_id' => $canSeeVersionCheckListItem['id']
                    ];
                }
                $reviewer['param'] = $tmpPassCheckListData;
            }
        }
    }

    /**
     * 获取任务最大版本列表
     * @param $param
     * @return array
     */
    public function getTaskMaxVersionList($param)
    {
        $fields = $param['fields'] ?? "id,number,link_id,is_pass,is_publish,description,is_uploaded,source_file_is_download,source_file_url,source_file_password";
        $page = $param['page'] ?? [1, C('default_page_total')];

        $res = [
            'total' => 0,
            'rows' => []
        ];

        list($moduleIds, $versionSql, $filter) = $this->dealTaskMaxVersionFilterData($param);
        // 总数记录
        $res['total'] = $this->getTaskMaxVersionCount($versionSql, $filter, $moduleIds, "count(*) as count")[0]['count'] ?? 0;
        if (empty($res['total'])) {
            return $res;
        }

        $taskList = model(TaskModel::class)
            ->join("entity on entity.id = task.entity_id")
            ->join("($versionSql) as version on task.id = version.link_id")
            ->where($filter)
            ->where([
                "entity.module_id" => ["IN", $moduleIds],
                "task.step_type" => TaskModel::STEP_TYPE_STEP
            ])
            ->page(...$page)
            ->field("task.id,task.step_category_id,entity.name,entity.initial_episode_code,entity.showings_number,JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as task_status,task.name as task_name")
            ->select();

        if (empty($taskList)) {
            return $res;
        }

        $taskIds = [];
        $stepCategoryIds = [];
        $statusIds = [];
        foreach ($taskList as $taskItem) {
            $taskIds[] = $taskItem['id'];
            $stepCategoryIds[$taskItem['step_category_id']] = $taskItem['step_category_id'];
            $statusIds[$taskItem['task_status']] = $taskItem['task_status'];
        }

        $stepCategoryIds = array_values($stepCategoryIds);
        $statusIds = array_values($statusIds);

        $stepCategoryList = model(StepCategoryModel::class)->where([
            'id' => ["IN", $stepCategoryIds]
        ])->field("id,name,code")->select();
        $stepCategoryMap = array_column($stepCategoryList, null, 'id');

        $statusList = model(StatusModel::class)->where([
            'id' => ["IN", $statusIds]
        ])->field("id,name,color,code,correspond,icon")->select();
        $statusMap = array_column($statusList, null, 'id');

        $versionList = $this->getMaxTaskListVersionList($taskIds, [], $fields);

        $versionMap = array_column($versionList, null, 'link_id');
        foreach ($taskList as $taskIndex => $taskItem) {
            $taskItem['step_category'] = $stepCategoryMap[$taskItem['step_category_id']] ?? null;
            $taskItem['version'] = $versionMap[$taskItem['id']] ?? null;
            $taskItem['status'] = $statusMap[$taskItem['task_status']] ?? null;
            $taskList[$taskIndex] = $taskItem;
        }

        $res['rows'] = $taskList;
        return $res;
    }

    /**
     * 处理筛选条件
     * @return array
     */
    public function dealTaskMaxVersionFilterData($param)
    {
        $filter = $param['filter'];
        $assetModuleIds = [module_by_code("asset")["id"], module_by_code("level")["id"]];
        $shotModuleIds = [module_by_code("shot")["id"]];

        $moduleIds = array_merge($assetModuleIds, $shotModuleIds);
        if (isset($param["module_code"])) {
            if ($param["module_code"] == "asset") {
                $moduleIds = $assetModuleIds;
            } else {
                $moduleIds = $shotModuleIds;
            }
        }

        $versionSql = "select link_id,max(version.id) as id from version GROUP BY link_id";
        $versionFilter = [];
        $noExistProject = true;
        foreach ($filter as $key => $value) {
            if ($key == "entity.project_id" || $key == "task.project_id") {
                $versionFilter[] = "project_id = $value";
                $noExistProject = false;
            }

            if ($key == "task_status") {
                $filter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"] = $value;
                unset($filter[$key]);
            }

            if ($key == "version.source_file_is_upload") {
                if ($value == "yes") {
                    $versionFilter[] = "version.source_file_url IS NOT NULL";
                } else {
                    $versionFilter[] = "version.source_file_url IS  NULL";
                }
                unset($filter[$key]);
            }

            if ($key == "version.source_file_is_download") {
                $versionFilter[] = "version.source_file_url IS NOT NULL";
                $versionFilter[] = "version.source_file_is_download = '$value'";
                unset($filter[$key]);
            }
        }

        //项目范围限制
        if ($noExistProject) {
            $projectList = model(ProjectModel::class)->where(['tenant_id' => request()->getTenantId()])->select();
            $projectId = join(",", array_column($projectList, 'id'));
            $versionFilter[] = "project_id in ($projectId)";
            $filter["entity.project_id"] = ["IN", $projectId];
        }

        //限定任务状态 (审核中,带客户审核)
        if (empty($filter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"])) {
            $reviewing = StatusService::getInstance()->getTaskDefaultReviewingStatus();
            $partyAReviewing = StatusService::getInstance()->getTaskWaitForPartyAReviewStatus();
            $filter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"] = ["IN", [$reviewing['id'], $partyAReviewing['id']]];
        }

        if (!empty($versionFilter)) {
            $versionWhere = join(" AND ", $versionFilter);

            $versionSql = "select link_id, id from version WHERE id in ( select max(id) as id from version GROUP BY link_id ) and  {$versionWhere}";
        }

        return [$moduleIds, $versionSql, $filter];
    }

    /**
     * 获取统计
     * @param $versionSql
     * @param $filter
     * @param $moduleIds
     * @param $fields
     * @param string $group
     * @return object|Model|RelationModel
     */
    public function getTaskMaxVersionCount($versionSql, $filter, $moduleIds, $fields, $group = "")
    {
        $countSql = model(TaskModel::class)
            ->join("entity on entity.id = task.entity_id")
            ->join("($versionSql) as version on task.id = version.link_id")
            ->where($filter)
            ->where([
                "entity.module_id" => ["IN", $moduleIds],
                "task.step_type" => TaskModel::STEP_TYPE_STEP
            ])
            ->field($fields);

        if (!empty($group)) {
            $countSql = $countSql->group($group);
        }

        return $countSql->select();
    }

    /**
     * 获取没有上传的统计
     * @param $param
     * @return Model|object|RelationModel
     */
    public function getNoUploadTaskMaxVersionCount($param)
    {
        if (empty($param['filter']['version.source_file_is_download'])) {
            $param['filter']['version.source_file_is_upload'] = $param['filter']['version.source_file_is_upload'] ?? 'no';
        }
        list($moduleIds, $versionSql, $filter) = $this->dealTaskMaxVersionFilterData($param);
        $shotModuleId = module_by_code("shot")['id'];
        $fields = "entity.initial_episode_code,count(task.id) as count,count(if(task.entity_module_id ={$shotModuleId},task.id,null)) as shot_count,count(if(task.entity_module_id <> {$shotModuleId},task.id,null)) as asset_count";
        return $this->getTaskMaxVersionCount($versionSql, $filter, $moduleIds, $fields, "entity.initial_episode_code");
    }
}
